/*
    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 2012  Jeremy W. Murphy <jeremy.william.murphy@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#ifndef ARITHMETIC_H
#define ARITHMETIC_H

#include "function.h"

#include <algorithm>
// #include <functional>


namespace rmitgprime
{
    /**
     * @brief This abstract class is here in case some general features of the subclasses can be sifted up.
     **/
    template <typename ReturnType = double, unsigned arity = 2u>
    class Arithmetic : public Function<ReturnType, arity>
    {
        public:
            virtual ReturnType operator*() const = 0;
    };


    template <typename ReturnType = double, unsigned arity = 2u>
    class Multiply : public Arithmetic<ReturnType, arity>
    {
        public:
            virtual ReturnType operator*() const
            {
                return std::accumulate(Function<ReturnType, arity>::children.begin() + 1,
                                       Function<ReturnType, arity>::children.end(),
                                       ***Function<ReturnType, arity>::children.begin(),
                                       [&](ReturnType const &ax, Node<ReturnType> const * const &bx) { return ax * **bx; });
            };
    };


    template <typename ReturnType = double, unsigned arity = 2u>
    class Add : public Arithmetic<ReturnType, arity>
    {
        public:
            virtual ReturnType operator*() const
            {
                return std::accumulate(Function<ReturnType, arity>::children.begin(),
                                    Function<ReturnType, arity>::children.end(),
                                    static_cast<ReturnType>(0), // Too cautious?
                                    [&](ReturnType const &ax, Node<ReturnType> const * const &bx) { return ax + **bx; });
            }
    };


    template <typename ReturnType = double, unsigned arity = 2u>
    class Subtract : public Arithmetic<ReturnType, arity>
    {
        public:
            virtual ReturnType operator*() const
            {
                return std::accumulate(Function<ReturnType, arity>::children.begin(),
                                    Function<ReturnType, arity>::children.end(),
                                    static_cast<ReturnType>(0),
                                    [&](ReturnType const &AX, Node<ReturnType> const * const &BX) { return AX - **BX; });
            }
    };


    template <typename ReturnType = double, unsigned arity = 2u>
    class Divide : public Arithmetic<ReturnType, arity>
    {
        public:
            virtual ReturnType operator*() const
            {
                return std::accumulate(Function<ReturnType, arity>::children.begin() + 1,
                                    Function<ReturnType, arity>::children.end(),
                                    ***Function<ReturnType, arity>::children.begin(),
                                    [&](ReturnType const &ax, Node<ReturnType> const * const &bx) { return ax / **bx; });
            }
    };
}

#endif // ARITHMETIC_H
